﻿using gt.rediscachemanager.Core.Executor;
using gt.rediscachemanager.Entry;
using gt.rediscachemanager.Utility;
using StackExchange.Redis;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Net;

namespace gt.rediscachemanager.Core.ClientServer
{
    public class RedisClientServer : IRedisExecutor
    {
        private IRedisExecutor m_executor;
        private RedisNode m_node;
        private IConnectionMultiplexer m_connection;

        public RedisNode Node { get { return this.m_node; } }
        public IRedisExecutor Executor { get { return this.m_executor; } }
        public IConnectionMultiplexer Connection { get { return this.m_connection; } }

        public event EventHandler<ClientServerConnectionFailedEventArgs> ClientServer_ConnectionFailedEvent;
        public event EventHandler<ClientServerConnectionRestoredEventArgs> ClientServer_ConnectionRestoredEvent;

        #region Construct

        public RedisClientServer(RedisNode node)
        {
            this.m_node = node;
            this.m_connection = ConnectionMultiplexer.Connect(node.RedisConnectOptions);
            this.m_connection.ConfigurationChanged += M_connection_ConfigurationChanged;
            this.m_connection.ConnectionFailed += M_connection_ConnectionFailed;
            this.m_connection.ConnectionRestored += M_connection_ConnectionRestored;

            this.m_executor = new StackExchangeRedisExecutor(this.m_connection, this.m_node.DB);
        }

        #endregion

        // todo check 是否会堵塞请求
        #region Event

        private void M_connection_ConnectionRestored(object sender, ConnectionFailedEventArgs e)
        {
            if (e.ConnectionType == ConnectionType.Subscription) return;
            try
            {
                Thread.Sleep(100);
                IServer s = Connection.GetServer(e.EndPoint);
                bool isSlave = s.IsSlave;
                string redisIp = RedisClientUtility.GetHostWithPort(e.EndPoint);
                LogUtility.Warn(string.Format("redisserver:{0} role:{1} restored.", redisIp, !isSlave ? "master" : "slave"));

                this.ClientServer_ConnectionRestoredEvent?.Invoke(this, new ClientServerConnectionRestoredEventArgs { IsMasterServer = !isSlave, ServerEndPoint = e.EndPoint });
            }
            catch (Exception ex)
            {
                LogUtility.Error("HandlerConnectionRestored error.", ex);
            }
        }
        private void M_connection_ConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {
            if (e.ConnectionType == ConnectionType.Subscription) return;

            try
            {
                IServer s = Connection.GetServer(e.EndPoint);
                int i = 1;
                bool reConnected = false;
                //延迟触发,check 是否是重新加载
                do
                {
                    if (s.IsConnected)
                    {
                        reConnected = true;
                        break;
                    }
                    Thread.Sleep(10);
                    i++;
                } while (i <= 10);

                if (!reConnected)
                {
                    bool isSlave = s.IsSlave;
                    string redisIp = RedisClientUtility.GetHostWithPort(e.EndPoint);
                    LogUtility.Warn(string.Format("redisserver:{0} role:{1} connection failed.failed type:{2}", redisIp, !isSlave ? "master" : "slave", e.FailureType.ToString()));

                    this.ClientServer_ConnectionFailedEvent?.Invoke(this, new ClientServerConnectionFailedEventArgs() { IsMasterServer = !isSlave, ServerEndPoint = e.EndPoint });
                }
            }
            catch (Exception ex)
            {
                LogUtility.Error("HandlerConnectionFailed error.", ex);
            }
        }
        private void M_connection_ConfigurationChanged(object sender, EndPointEventArgs e)
        {
            try
            {
                IServer s = Connection.GetServer(e.EndPoint);
                string cacheServerIp = RedisClientUtility.GetHostWithPort(e.EndPoint);
                LogUtility.Warn(string.Format("redisserver:{0} config changed,role:{1}.", cacheServerIp, s.IsSlave ? "slave" : "master"));
            }
            catch (Exception ex)
            {
                LogUtility.Error("HandlerConfigChanged error.", ex);
            }
        }

        #endregion

        public virtual TResult ExecuteCommand<T, TResult>(RedisCommand command, RedisMessageWrapper<T, TResult> message, CommandFlags commandFlags)
        {
            return this.m_executor.ExecuteCommand<T, TResult>(command, message, commandFlags);
        }

        public async Task<TResult> ExecuteCommandAsync<T, TResult>(RedisCommand command, RedisMessageWrapper<T, TResult> message, CommandFlags commandFlags)
        {
            return await this.m_executor.ExecuteCommandAsync<T, TResult>(command, message, commandFlags).ConfigureAwait(false);
        }

        public EndPoint GetCurrentMaster()
        {
            if (this.m_connection == null || this.m_node == null) return null;
            return Connection.GetEndPoints().FirstOrDefault(x => !this.m_connection.GetServer(x).IsSlave);
        }
        public EndPoint[] GetCurrentSlaves()
        {
            if (this.m_connection == null || this.m_node == null) return null;
            return Connection.GetEndPoints().Where(x => this.m_connection.GetServer(x).IsSlave).ToArray();
        }
    }
}